package heap

import (
	"jvm/classfile"
)

type SymRef struct {
	constantPool *ConstantPool
	className    string
	class        *Class
}

// 返回类引用对应的类结构
func (self *SymRef) ResolvedClass() *Class {
	if self.class == nil {
		self.resolvedClassRef()
	}
	return self.class
}
func (self *SymRef) resolvedClassRef() {
	currentClass := self.constantPool.class
	refClass := currentClass.loader.LoadClass(self.className)
	if refClass.isAccessibleTo(currentClass) {
		self.class = refClass
	} else {
		panic("java/lang/IllegalAccessError")
	}
}

// **************************************************
type ClassRef struct {
	SymRef
}

func newClassRef(cp *ConstantPool, classInfo *classfile.ConstantClass) *ClassRef {
	ref := &ClassRef{}
	ref.constantPool = cp
	ref.className = classInfo.ClassName()
	return ref
}

// **************************************************
type MemberRef struct {
	SymRef
	name       string
	descriptor string
}

func (self *MemberRef) Name() string {
	return self.name
}
func (self *MemberRef) Descriptor() string {
	return self.descriptor
}
// **************************************************
type FieldRef struct {
	MemberRef
	field *Field
}

func newFieldRef(cp *ConstantPool, fieldInfo *classfile.ConstantFieldref) *FieldRef {
	fieldRef := &FieldRef{}
	fieldRef.constantPool = cp
	fieldRef.className = fieldInfo.GetConstantClass().ClassName()
	fieldRef.name = fieldInfo.GetNameAndType().Name()
	fieldRef.descriptor = fieldInfo.GetNameAndType().Descriptor()
	return fieldRef
}

func (self *FieldRef) ResolvedField() *Field {
	if self.field == nil {
		self.resolvedFieldRef()
	}
	return self.field
}

func (self *FieldRef) resolvedFieldRef() {
	currentClass := self.constantPool.class
	self.ResolvedClass()
	field := findField(self.class, self.name, self.descriptor)
	if field == nil {
		panic("java.lang.NoSuchFieldError")
	}
	if !field.IsAccessibleTo(currentClass) {
		panic("java.lang.IllegalAccessError")
	}
	self.field = field
}

// 在class,class实现的接口，class的父类中递归的找出其字段表中符合类型和名称的字段
func findField(class *Class, name string, descriptor string) *Field {
	for _, filed := range class.fields {
		if filed.name == name && filed.descriptor == descriptor {
			return &filed
		}
	}
	for _, iface := range class.interfaces {
		if field := findField(iface, name, descriptor); field != nil {
			return field
		}
	}
	if class.superClass != nil {
		return findField(class.superClass, name, descriptor)
	}
	return nil
}

// **************************************************
type MethodRef struct {
	MemberRef
	method *Method
}

func newMethodRef(cp *ConstantPool, methodInfo *classfile.ConstantMethodref) *MethodRef {
	methodRef := &MethodRef{}
	methodRef.constantPool = cp
	methodRef.className = methodInfo.GetConstantClass().ClassName()
	methodRef.name = methodInfo.GetNameAndType().Name()
	methodRef.descriptor = methodInfo.GetNameAndType().Descriptor()
	return methodRef
}

// **************************************************
type InterfaceMethodRef struct {
	MemberRef
	method *Method
}

func newInterfaceMethodRef(cp *ConstantPool, interfaceMethodInfo *classfile.ConstantInterfaceMethodref) *InterfaceMethodRef {
	interfaceMethodRef := &InterfaceMethodRef{}
	interfaceMethodRef.constantPool = cp
	interfaceMethodRef.className = interfaceMethodInfo.GetConstantClass().ClassName()
	interfaceMethodRef.name = interfaceMethodInfo.GetNameAndType().Name()
	interfaceMethodRef.descriptor = interfaceMethodInfo.GetNameAndType().Descriptor()
	return interfaceMethodRef
}

// **************************************************
